package net.borlis.sample.horses.core;

import java.util.*;


/**
 * Created by Alexander on 21.12.2014.
 */
public class Inventory extends HashMap<Denominator, Integer> {

    public Inventory() {
    }

    public Inventory(Integer sum) {
        List<Denominator> denominationsList = new ArrayList<Denominator>(Arrays.asList(Denominator.values()));
        Collections.sort(denominationsList, new Comparator<Denominator>() {
            @Override
            public int compare(Denominator o1, Denominator o2) {
                return -(o1.value().compareTo(o2.value()));
            }
        });
        for (Denominator denominator : denominationsList) {
            if (sum != 0) {
                sum = process(denominator, sum);
            }
            if (!this.keySet().contains(denominator)) {
                this.put(denominator, 0);
            }
        }
        if (sum != 0) {
            throw new IllegalStateException("Wrong sum of payments provided!");
        }
    }

    private Integer process(Denominator denomination, Integer sum) {
        int descr = (int) Math.floor(sum / denomination.value());
        if (descr >= 1) {
            this.put(denomination, descr);
            sum = sum - descr * denomination.value();
        }
        return sum;
    }

    public Integer bank() {
        Set<Map.Entry<Denominator, Integer>> entries = this.entrySet();
        Integer summ = 0;
        for (Map.Entry<Denominator, Integer> entry : entries) {
            summ = summ + entry.getKey().value() * entry.getValue();
        }
        return summ;
    }

    public void pay(Integer summ) {
        Integer currentSum = bank();
        System.out.println("Dispensing:");
        if (currentSum < summ) {
            throw new IllegalStateException("No money to pay!");
        } else if (currentSum.equals(summ)) {
            List<Denominator> denominationsList = Arrays.asList(Denominator.values());
            for (Denominator denominations : denominationsList) {
                makeZero(denominations);
            }
        }
        Inventory dispersion = new Inventory(summ);
        List<Denominator> denominationsList = new ArrayList<Denominator>(Arrays.asList(Denominator.values()));
        Collections.sort(denominationsList, new Comparator<Denominator>() {
            @Override
            public int compare(Denominator o1, Denominator o2) {
                return -(o1.value().compareTo(o2.value()));
            }
        });
        for (Denominator denomination : denominationsList) {
            dispersion = process(denomination, dispersion, summ);
        }
    }

    private Inventory process(Denominator denominator, Inventory other, Integer summ) {
        if (same(denominator, other)) {
            if (!other.get(denominator).equals(0)) {
                System.out.println("$" + denominator.value() + ": " + other.get(denominator));
            }
            makeZero(denominator);
        } else if (moreThenEnough(denominator, other)) {
            this.put(denominator, this.get(denominator) - other.get(denominator));
            if (!other.get(denominator).equals(0)) {
                System.out.println("$" + denominator.value() + ": " + other.get(denominator));
            }
        } else if (notEnough(denominator, other) && denominator.hasLower()) {
            Integer difference = other.get(denominator) - this.get(denominator);
            makeZero(denominator);
            this.put(denominator, other.get(denominator.lower()) + difference * denominator.multiplier());
        } else if (notEnough(denominator, other) && !denominator.hasLower()) {
            System.out.println("Insufficient Funds: " + summ);
        }
        return other;
    }

    private void makeZero(Denominator denominator) {
        this.put(denominator, new Integer(0));
    }

    private Boolean same(Denominator denominator, Inventory other) {
        return other.get(denominator).equals(this.get(denominator));
    }

    private Boolean moreThenEnough(Denominator denominator, Inventory other) {
        return this.get(denominator) > other.get(denominator);
    }

    private Boolean notEnough(Denominator denominator, Inventory other) {
        return this.get(denominator) < other.get(denominator);
    }

    public void printBank() {
        System.out.println(" ==== Current Bank Statements ====");
        Set<Map.Entry<Denominator, Integer>> current = this.entrySet();
        Integer bankTotal = 0;
        for (Map.Entry<Denominator, Integer> entry : current) {
            System.out.println(entry.getKey().value() + "$ coupure amount is " + entry.getValue());
            bankTotal = bankTotal + entry.getKey().value() * entry.getValue();
        }
        System.out.println(" ==== Bank Total is : " + bankTotal + " ====");
    }

    public void addToBank(Integer amount) {
        Inventory newInv = new Inventory(amount);
        Set<Map.Entry<Denominator, Integer>> set = newInv.entrySet();
        for (Map.Entry<Denominator, Integer> entry : set) {
            if(!entry.getValue().equals(0)) {
                Integer current = this.get(entry.getKey());
                this.put(entry.getKey(), current + entry.getValue());
            }
        }
        printBank();
    }

}
